
您查询的关键词是：laravelpipeline
如果打开速度慢，可以尝试快速版；如果想更新或删除快照，可以投诉快照。

百度和网页 https://www.jianshu.com/p/3c2791a525d0 的作者无关，不对其内容负责。百度快照谨为网络故障时之索引，不代表被搜索网站的即时页面。
首页
下载APP
搜索
理解Laravel中的pipeline
suoga
理解Laravel中的pipeline

suoga
0.2152015.09.08 00:00:57字数 1,533阅读 8,273
pipeline在laravel的启动过程中出现次数很多，要了解laravel的启动过程和生命周期，理解pipeline就是其中的一个关键点。网上对pipeline的讲解很少，所以我自己写一写吧。
首先还是来看看调用栈，也就是从一个请求开始到返回响应，laravel都干了些什么。
在路由中配置如下

Route::get('/', function() {
    return debug_backtrace();
});
然后启动laravel自带的web server

php artisan serve

启动laravel自带的serve

然后访问localhost:8000/
可以看到打印的调用栈，很长……

打印调用栈

左下角可以看出，从请求开始到结束，尽管什么都没干，但是依然加载了39个类……那些追求卓越性能的同学们需要好好考虑一下……
我们的重点是pipeline，如果你按照上面的步骤看了，会发现有一个pipeline调用的次数非常多。
那它到底是做什么用的呢？
简单一点讲，它是为了实现Laravel中的middleware。仔细想想middleware的代码

public function handle($request, Closure $next) {
    //do something for $request
    return $next($request);
}
一个请求经历层层的中间件的处理，才得到最终的请求，这到底是实现的呢？答案就是pipeline
首先来宏观感受下pipeline，到底怎么用的，再去细说它



pipeline在源码的使用

可以看到，主要有3个方法，也是pipeline暴露给使用者的所有方法

send
through
then
send方法
/** 
 * Set the object being sent through the pipeline.
 * 
 * @param  mixed  $passable 
 * @return $this 
*/
public function send($passable) {    
    $this->passable = $passable;    
    return $this;
}
从说明看就是被在pipeline中传递的对象，可能不太好理解。那么我们就假定pipeline使用在middleware中，那么这个send的对象就是$request
想想看上面的中间件代码，每个handle方法接收的第一个参数是$request，其实就是这儿设置的讲被send的对象，它会在之后被到处传递，A处理完了送给B处理再送给C处理……pipeline，形象吧

through

屏幕快照 2015-09-07 下午9.05.36.png

顾名思义：通过、经由，就是上面send设置的对象要经由哪些中间件来处理
上面截图宏观说明pipeline的用法的时候可以看到，在调用through的时候
(new Pipeline($this))
    ->send($request)
    ->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware)
    ->then($this->dispatchToRouter());
看through里传的参数，很好懂，如果当前请求我们设置了不需要中间件，那么就传一个空数组，不然就传递我们预先定义好的中间件数组
pipeline里的代码如下

/** 
  * Set the array of pipes. 
  * 
  * @param  dynamic|array  $pipes 
  * @return $this 
*/
public function through($pipes){    
    $this->pipes = is_array($pipes) ? $pipes : func_get_args();    
    return $this;
}
这个看起来也很简单，你要么传一个数组，要么传多个字符串，它会拼成一个数组，无所谓，反正指明中间件的名字就好

then
接下来就是核心，也是代码晦涩的地方了。
then方法就是当一切准备就绪后，下达开始处理的命令。
我们前面也能看到，send和through都是设置，并没有做其他什么，所以当 要传递的对象、需要经历的中间件都设置好之后，then?当然就是开始处理咯！
还是先看看then的代码

/** * Run the pipeline with a final destination callback. 
  * 
  * @param  \Closure  $destination 
  * @return mixed 
*/
public function then(Closure $destination){    
    $firstSlice = $this->getInitialSlice($destination);    
    $pipes = array_reverse($this->pipes);    
    return call_user_func(        
        array_reduce($pipes, $this->getSlice(), $firstSlice), 
        $this->passable);
    );
}
看起来短短几行，但是要理解起来可不太容易（这也是我比较喜欢laravel的原因，代码简单而优雅，一般人欣赏不来，哈哈）
我先大概讲一下
then方法需要接收一个匿名函数 $destination，至于什么样的匿名函数，你先可以不用管，往后看。我一句一句解释

public function then(Closure $destination){    
    //把传进来的匿名函数包装成一个闭包，不太理解没关系，反正知道$firstSlice是一个函数就好了
    $firstSlice = $this->getInitialSlice($destination);    
    //把through方法传进来的参数，也就是中间件数组反转，为什么要这么做？
    //简单讲是利用栈的特性，继续往下看就知道了
    $pipes = array_reverse($this->pipes);  
    //array_reduce(...)返回一个函数A，然后执行这个函数，把send进来的对象当参数传递函数A
    return call_user_func(        
        array_reduce($pipes, $this->getSlice(), $firstSlice), 
        $this->passable);
    );
}
然后呢？
没有然后了，这就完成了整个工作，是不是很优雅（坑爹）的代码，哈哈
那我们的精力就放到这个array_reduce上面，它到底返回了一个什么神奇的函数，我只要把$request传递给它，就过完了所有中间件？
array_reduce($arr, 'funcA',5)大致等价于，第二个参数可以是函数名的字符串

function fake_array_reduce($arr, $func, $initial=null)
    $temp = $initial;
    foreach($arr as $v) {
        $temp = $func($temp, $v);
    }
    return $temp;
}
理解意思即可
回到then的源码，代码中传递的第二个参数一个是一个函数，而它是通过执行$this->getSlice()获得的一个函数，我们来看看这个getSlice方法

/** 
  * Get a Closure that represents a slice of the application onion. 
  * 
  * @return \Closure 
  */
protected function getSlice(){    
    return function ($stack, $pipe) {        
        return function ($passable) use ($stack, $pipe) {          
            if ($pipe instanceof Closure) {               
                 return call_user_func($pipe, $passable, $stack);            
            } else {                
                list($name, $parameters) = $this->parsePipeString($pipe);
                return call_user_func_array(
                    [$this->container->make($name), $this->method],    
                    array_merge([$passable, $stack], $parameters)
                );            
            }        
        };    
    };
}
刚看到这里我也有点头晕，这闭包套闭包的，什么鬼！
我们在简化一下，就是调用$this->getSlice()之后得到了一个函数，假设是函数B，函数B接收两个参数，并返回一个函数C
我们来大致模拟一下这个过程
写一段假代码，仅仅是为了更好地说明这个过程：

function haha($stack, $middleware) {
    return function($param) use ($stack, $middleware){
        //do something 
    };
}
function fake_array_reduce($middlewares, $func, $inital=null) {
    $temp = $initial;
    //假设传递进来的函数$func = 'haha'
    foreach($middlewares as $middleware) {
        $temp = $func($temp, $middleware);
    }
    return $temp;
}
这个过程的基本流程如上，haha返回了一个空函数(实际上肯定不是),我只是为了说明每次都返回一个函数作为下一次迭代的参数，而并没有执行任何其它功能，按照上面的代码，最后执行完fake_array_reduce会得到一个函数,这个函数接收一个参数。
所以再回过头来看
call_user_func(array_reduce($pipes, $this->getSlice(), $firstSlice), $this->passable);
实际上就是相当于调用了array_reduce最后一次迭代返回的函数，然后给这个函数传递了一个参数$this->passable
所以，这时候我们需要关注最后一次返回的函数是什么，看看源码关键是这段

 return function($stack, $pipe) {
    return function($passable) use ($stack, $pipe) {
        if($pipe instanceof Closure) {
            return call_user_func($pipe, $passable, $stack);
        } else {
            //..
        }
    }
}
每次迭代传入了当前的$pipe和上一次迭代返回的函数


IMG_0687.JPG

由上图所示
由于把pipes先反转了一下，所以最后一次迭代array_reduce得到的函数f3所use的是($stack=f2, $pipe=$middleware[0])
那么call_user_func(f3,$this->passable)相当于是
return call_user_func($middleware[0]->handle, $this->passable, f2);
仔细想想，middleware里面的handle方法

public function handle($request, Closure $next) {
    //...
    return $next($request);
}
在这里就相当于就是return f2($request)
也就相当于return call_user_func($middleware[1]->handle, $request, f1)
而这个$request是经过middleware[0]处理过后传递过来的。
……
这个过程比较绕，需要大家自己再画图理解一下，比如源代码里用到的onion（洋葱），$stack（栈），这些单词可以帮助大家更好地理解这个过程。
所以return call_user_func($pipes, $this->getSlice(), $this->passable)就会把$request请求按middleware定义的先后顺序，流水线处理，中间件处理完了之后再传递给$this->dispatchToRouter()这个方法处理（这又是一个抽象），从而得到最后的响应结果。

理解pipeline需要理解 闭包 栈等概念，熟悉了pipeline会对理解php的新特性有比较大的帮助。

That's all thanks
laravel
  
suogacharming
总资产22 (约1.94元)共写了6.0W字获得97个赞共87个粉丝
推荐阅读更多精彩内容
Laravel学习之启动过程
先说几句废话，调和气氛。事情的起由来自客户需求频繁变更，伟大的师傅决定横刀立马的改革使用新的框架（created ...